package com.ficus.road.maintain.ditalTwin.controller;

import com.ficus.road.maintain.core.exception.InvalidRequestException;
import com.ficus.road.maintain.core.model.*;
import com.ficus.road.maintain.core.model.common.ErrorCodeDTO;
import com.ficus.road.maintain.core.model.common.ErrorDTO;
import com.ficus.road.maintain.core.model.marker.AssetMapIdRequestDTO;
import com.ficus.road.maintain.core.model.marker.HitMapIdRequestDTO;
import com.ficus.road.maintain.core.utils.QueryFilterCommonConverter;
import com.ficus.road.maintain.ditalTwin.dao.AssetDao;
import com.ficus.road.maintain.ditalTwin.dao.DigitalHitDao;
import com.ficus.road.maintain.ditalTwin.dao.FrameDao;
import com.ficus.road.maintain.core.model.digitalTwin.Asset;
import com.ficus.road.maintain.core.model.digitalTwin.DigitalRoadInfo;
import com.ficus.road.maintain.core.model.digitalTwin.Frame;
import com.ficus.road.maintain.core.model.digitalTwin.request.DigitalTwinProfileResponse;
import com.ficus.road.maintain.core.model.digitalTwin.request.ElementStatistic;
import com.ficus.road.maintain.core.model.digitalTwin.request.RoadSearchRequest;
import com.ficus.road.maintain.ditalTwin.service.DigitalHitService;
import com.ficus.road.maintain.ditalTwin.service.DigitalRoadService;
import com.ficus.road.maintain.util.DateUtil;
import com.ficus.road.maintain.util.MyDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.util.*;

@RestController
@Slf4j
public class DigitalTwinController {
    @Autowired
    private FrameDao frameDao;
    @Autowired
    private DigitalRoadService digitalRoadService;

    @Autowired
    DigitalHitService hitService;

    @Autowired
    AssetDao assetDao;

    @Autowired
    DigitalHitDao digitalHitDao;

    @GetMapping("/digitalTwin/assets")
    public ResponseEntity<List<Frame>> assets() {
        List<Frame> all = frameDao.findAll();
        for (Frame frame : all) {
            for (Asset asset : frame.getAssets()) {
                asset.setCreationTime(DateUtil.after(asset.getCreationTime(), Calendar.MONTH, 4));
                asset.setTimestamp(asset.getCreationTime().getTime());
            }
        }
        return ResponseEntity.ok(all);
    }

    @PostMapping("/v1/digitalTwin/road/list")
    public ResponseEntity<List<DigitalRoadInfo>> roadList(@RequestBody RoadSearchRequest request) {
        return ResponseEntity.ok(digitalRoadService.roadList());
    }

    @GetMapping("/v1/digitalTwin/road/{roadId}")
    public ResponseEntity<DigitalRoadInfo> roadDetail(@PathVariable String roadId) {
        DigitalRoadInfo road = digitalRoadService.getRoad(roadId);
        Query query = new Query(Criteria.where("hit_time")
                .gte(MyDateUtil.getStartOfDay(new Date()).getTime()/1000).lt(MyDateUtil.getEndOfDay(new Date()).getTime()/1000));
        road.setHitCountToday(hitService.countHits(query));
        road.setHitCount(hitService.countHits(new Query()));
        return ResponseEntity.ok(road);
    }

    @GetMapping("/v1/digitalTwin/profile")
    public ResponseEntity<DigitalTwinProfileResponse> profile() {
        return ResponseEntity.ok(digitalRoadService.profile());
    }

    @GetMapping("/v1/digitalTwin/element/all")
    public ResponseEntity<List<ElementStatistic>> elementAll() {
        return ResponseEntity.ok(digitalRoadService.elementStatistic(null));
    }

    @GetMapping("/v1/digitalTwin/element/{roadId}")
    public ResponseEntity<List<ElementStatistic>> elementByRoadId(@PathVariable String roadId) {
        return ResponseEntity.ok(digitalRoadService.elementStatistic(roadId));
    }

    @PostMapping(value = "/v1/digitalTwin/element/mapId")
    public ResponseEntity assetMapId(@RequestBody AssetMapIdRequestDTO request) {
        String assetId = request.getAssetId();
        Asset asset = assetDao.findOne(new Query(Criteria.where("track_id").is(assetId)));
        if (asset == null) {
            ErrorDTO err = ErrorDTO.builder().message("请求参数有误,无assetId:" + assetId + "对应数据").retryable(false).code(ErrorCodeDTO.INVALID_PARAMETER).build();
            throw new InvalidRequestException(err);
        }
        asset.setMapId(request.getMapId());
        assetDao.saveOrUpdate(asset);
        return new ResponseEntity<>(null, HttpStatus.OK);
    }

    @PostMapping(value = "/v1/digitalTwin/hits/mapId")
    public ResponseEntity hitMapId(@RequestBody HitMapIdRequestDTO request) {
        String hitId = request.getHitId();
        Hit hit = digitalHitDao.fetch(hitId);
        if (hit == null) {
            ErrorDTO err = ErrorDTO.builder().message("请求参数有误,无hitId:" + hitId + "对应数据").retryable(false).code(ErrorCodeDTO.INVALID_PARAMETER).build();
            throw new InvalidRequestException(err);
        }
        hit.setMapId(request.getMapId());
        digitalHitDao.saveOrUpdate(hit);
        return new ResponseEntity<>(null, HttpStatus.OK);
    }

    //按指定条件查询病害
    @RequestMapping(value = "/v1/digitalTwin/hits/query", method = RequestMethod.POST)
    public ResponseEntity<List<Hit>> queryHit(@Min(0) @Valid @RequestParam(defaultValue = "0") Integer start,
                                              @Min(1) @Valid @RequestParam(defaultValue = "1000") Integer limit,
                                              @Valid @RequestBody HitQueryRequestDTO hitQueryRequest)
            throws IllegalAccessException {
        Query query = toHitQuery(hitQueryRequest.getFilter());
        query.with(new Sort(Sort.Direction.DESC, "attrs.is_top")).with(new Sort(Sort.Direction.DESC, "hit_time"));
        query.skip(start).limit(limit);
        List<Hit> hits = hitService.queryHits(query);
        //根据id查符合条件的总数
        query.fields().include("_id");
        //最多返回一万条数据,防止无限分页
        query.skip(0).limit(10000);
        int total = hitService.countHits(query);
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        Boolean isFinal = Boolean.FALSE;
        if (hits.size() != limit) {
            isFinal = Boolean.TRUE;
        }
        headers.add("X-OneAPI-Exhausted", String.valueOf(isFinal));
        headers.add("X-OneAPI-EstimatedSize", String.valueOf(total));
        return new ResponseEntity<>(hits, headers, HttpStatus.OK);
    }

    //获取一个告警信息
    @RequestMapping(value = "/v1/digitalTwin/hits/{id}",
            method = RequestMethod.GET)
    public ResponseEntity<Hit> getHit(@PathVariable("id") String id) {
        Hit hit = hitService.getHit(id);
        hit.setList(null);
        return new ResponseEntity<>(hit, HttpStatus.OK);
    }

    //返回满足查询条件的数量
    @RequestMapping(value = "/v1/digitalTwin/hits/query/total",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.POST)
    public ResponseEntity<TotalResult> queryTotal(@Valid @RequestBody HitQueryRequestDTO hitQueryRequest) throws IllegalAccessException {
        Query query = toHitQuery(hitQueryRequest.getFilter());
        query.fields().include("_id");
        query.skip(0).limit(10000);
        int total = hitService.countHits(query);
        TotalResult totalResult = new TotalResult();
        totalResult.setTotal(total);
        return new ResponseEntity<>(totalResult, HttpStatus.OK);
    }
    public static Query toHitQuery(HitQueryFilterDTO filter) throws IllegalAccessException {
        List<Criteria> criteriaList = new ArrayList<>();
        QueryFilterCommonDTO statusFilter = new QueryFilterCommonDTO();
        statusFilter.setNotEq("DELETE");
        criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.confirmation.status", statusFilter));
        // 如果是审核页面，不加其他限制
        if (Objects.nonNull(filter)){
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getConfirmation())
                    && filter.getConfirmation().getHashEq() != null
                    && filter.getConfirmation().getHashEq().equals(Confirmation.ConfirmationStatus.TO_BE_CONFIRMED.toString())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.confirmation.status", filter.getConfirmation()));
                //病害来源
                if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getSource())) {
                    criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.source", filter.getSource()));
                }
                Criteria criteria = new Criteria();
                if (!criteriaList.isEmpty()) {
                    criteria.andOperator(criteriaList.toArray(new Criteria[criteriaList.size()]));
                }
                return new Query(criteria);
            }

            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getConfirmation())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.confirmation.status", filter.getConfirmation()));
            }
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getScene())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("scene", filter.getScene()));
            }
            //时间筛选
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getHitTime())) {
                Criteria criteria = Criteria.where("first_hit_time");
                if (Objects.nonNull(filter.getHitTime().getHashLt())) {
                    criteria.lt(filter.getHitTime().getHashLt());
                }
                if (Objects.nonNull(filter.getHitTime().getHashGt())) {
                    criteria.gt(filter.getHitTime().getHashGt());
                }
                if (Objects.nonNull(filter.getHitTime().getHashLte())) {
                    criteria.lte(filter.getHitTime().getHashLte());
                }
                if (Objects.nonNull(filter.getHitTime().getHashGte())) {
                    criteria.gte(filter.getHitTime().getHashGte());
                }
                criteriaList.add(criteria);
            }
            //处置撞状态
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getStatus())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.status", filter.getStatus()));
            }
            //处置公司
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getHandleCompanyCode())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.handle_company_code", filter.getHandleCompanyCode()));
            }
            //闭环类型
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getLoop())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.loop", filter.getLoop()));
            }
            //病害来源
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getSource())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.source", filter.getSource()));
            }
            //病害类型
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getType())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("type", filter.getType()));
            }

  if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getCarIds())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.car_id", filter.getCarIds()));
            }
        }

        Criteria criteria = new Criteria();
        if (!criteriaList.isEmpty()) {
            criteria.andOperator(criteriaList.toArray(new Criteria[criteriaList.size()]));
        }
        return new Query(criteria);
    }
}
